<?php
// +----------------------------------------------------------------------
// | Variable 自定义服务
// +----------------------------------------------------------------------
// | 主要服务于Variable数据处理
// +----------------------------------------------------------------------
// | 错误编码头 230xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\model\AttributeModel;
use common\model\EntityModel;
use common\model\FieldModel;
use common\model\ModuleModel;
use support\bootstrap\Redis;
use support\Db;
use support\SingletonTrait;
use think\Model;
use think\module\Module;

class FieldService
{

    // 上一次刷新字段时间
    const MODULE_CONFIG_REFRESH_KEY = 'module_config_fresh_time_key';
    // 上一次生成内存字段缓存时间
    private static int $moduleFieldMakeTime = 0;

    /**
     * @var \common\model\FieldModel
     */
    protected $fieldModel;

    // 排除模块
    protected $exclusionModule = ['tenant', 'module'];


    // 单例工具
    use SingletonTrait;

    public function __construct()
    {
        $this->fieldModel = model(FieldModel::class);
    }

    /**
     * 设置缓存生成时间
     * @param int $moduleFieldMakeTime
     */
    public static function setModuleFieldMakeTime(int $moduleFieldMakeTime): void
    {
        self::$moduleFieldMakeTime = $moduleFieldMakeTime;
    }

    /**
     * 获取table list
     * @return mixed
     */
    public function getTableList()
    {
        // 获取固定表和自定义表列表
        $moduleModel = model(ModuleModel::class);
        $resData = $moduleModel->field('id,type,code')->select();
        return $resData;
    }

    /**
     * 获取单张表配置
     * @param $tableName
     * @return array
     */
    public function getFieldConfig($tableName = '')
    {
        if (!empty($tableName)) {
            // 判断是否传入表名称
            $resData = $this->fieldModel->getTableFields($tableName);
        } else {
            $resData = ["total" => 0, "rows" => []];
        }
        return $resData;
    }

    /**
     * 修改
     * @param string $tableName
     * @param array $config
     * @return array
     * @throws \think\Exception
     */
    public function modifyFieldConfig($tableName = '', $config = [])
    {
        $resData = $this->fieldModel->modifyFieldConfig($tableName, $config);
        return $resData;
    }

    /**
     * 创建自定义字段
     * @param $param
     * @return mixed
     * @throws \think\Exception
     */
    public function createCustomField($param)
    {
        $resData = $this->fieldModel->createCustomField($param);
        return $resData;
    }


    /**
     * 获取自定义字段类型列表
     * @return array
     */
    public function getCustomFieldTypeList()
    {
        $listData = [
            [ // 输入框
                "id" => "input",
                "name" => "input"
            ],
            [ // 标签
                "id" => "tag",
                "name" => "tag"
            ],
            [ // link 链接
                "id" => "link",
                "name" => "link"
            ],
            [ // 下拉框
                "id" => "select",
                "name" => "select"
            ],
            [ // 多行文本
                "id" => "text_area",
                "name" => "text_area"
            ],
            [ // 富文本
                "id" => "rich_text",
                "name" => "rich_text"
            ],
            [ // 上传
                "id" => "upload",
                "name" => "upload"
            ],
            [ // 工时
                "id" => "times",
                "name" => "times"
            ],
            [ // 时间日期
                "id" => "date",
                "name" => "date"
            ],
            [ // 开关
                "id" => "switch",
                "name" => "switch"
            ],
            [ // 单选框
                "id" => "radio",
                "name" => "radio"
            ],
            [ // 复选框
                "id" => "checkbox",
                "name" => "checkbox"
            ],
            [ // 水平关联
                "id" => "horizontal_relation",
                "name" => "horizontal_relation"
            ],
            [ // 规格
                "id" => "attribute",
                "name" => "attribute"
            ]
        ];

        return $listData;
    }

    /**
     * 获取自定义下拉框可以关联模块列表数据
     * @return array
     */
    public function getSelectModuleList()
    {
        $cantHorizontalModuleCodeList = ['user,status,tag,media'];

        $moduleModel = model(ModuleModel::class);
        $listData = $moduleModel->field('id,type,name,code')->where(["code" => ["IN", join(',', $cantHorizontalModuleCodeList)]])->select();

        return $listData;
    }

    /**
     * 获取自定义水平可以关联模块列表数据
     * @return array
     */
    public function getHorizontalModuleList()
    {
        $cantHorizontalModuleCodeList = ['user,status,media,tag'];

        $moduleModel = model(ModuleModel::class);
        $listData = $moduleModel->field('id,type,name,code')->where(["code" => ["IN", join(',', $cantHorizontalModuleCodeList)]])->select();

        return $listData;
    }

    /**
     * 递归处理
     * @param $relationModuleFields
     * @param $relationItem
     */
    protected function recursiveExcelImportFields(&$relationModuleFields, $relationItem)
    {
        if ($relationItem['filter_type'] === 'entity') {
            $moduleCode = $relationItem['belong_module'];
        } else {
            $moduleCode = $relationItem['module_code'];
        }

        if (!in_array($moduleCode, $this->exclusionModule)) {
            $tempFields = $this->getFieldConfig($moduleCode);
            $allowEditField = [];

            foreach ($tempFields as $field) {
                if ($field['edit'] === 'allow') {
                    $allowEditField[] = $field;
                }
            }

            $relationModuleFields[$moduleCode] = $allowEditField;

            if (array_key_exists('child', $relationItem) && !empty($relationItem['child'])) {
                $this->recursiveExcelImportFields($relationModuleFields, $relationItem['child']);
            }
        }
    }

    /**
     * 获取指定模块的字段
     * @param $moduleCode
     * @return array
     * @throws \think\Exception
     */
    public function getExcelImportFields($moduleCode)
    {
        // 获取指定模块字段
        $relationModel = model(EntityModel::class);
        $relationModel->resetDefault();
        $relationModel->setCurrentModuleCode($moduleCode);
        $relationModule = $relationModel->parserFilterModuleRelation(true);

        // 不允许导入模块
        $skipModule = [module_by_code('media')['id']];
        $relationModuleFields = [];
        foreach ($relationModule as $relationItem) {
            if (in_array($relationItem['dst_module_id'], $skipModule)) {
                continue;
            }
            // 过滤掉media模块 media都是上传的不允许导入
            $this->recursiveExcelImportFields($relationModuleFields, $relationItem);
        }
        $relationModel->resetDefault();

        return $relationModuleFields;
    }


    /**
     * 生成模块字段缓存
     * @return array
     */
    public function generateModuleFieldCache()
    {
        // 获取所有注册模块
        $moduleModel = model(ModuleModel::class);
        $moduleList = $moduleModel->selectData(['fields' => 'id,type,name,code,uuid']);

        $moduleDictData = [];
        $moduleDictData['module_index_by_id'] = array_column($moduleList["rows"], null, 'id');
        $moduleDictData['module_index_by_code'] = array_column($moduleList["rows"], null, 'code');


        // 获取所有模块字段
        $fieldModel = model(FieldModel::class);
        $moduleDictData['field_index_by_code'] = $fieldModel->getAllModuleFieldsMapData($moduleDictData['module_index_by_id']);

        return $moduleDictData;
    }


    /**
     * 自动刷新字典配置和缓存
     * @throws \think\Exception
     */
    public function autoFixFieldConfigAndCache()
    {
        $moduleModel = model(ModuleModel::class);
        $moduleList = $moduleModel->field('code')->select();

        // 获取最新字段配置然后写回去
        foreach ($moduleList as $item) {
            $newFields = $this->getFieldConfig($item['code']);

            // 分开固定字段和自定义字段
            $saveFields = [
                'custom' => [],
                'fixed' => []
            ];
            foreach ($newFields as $field) {
                if ($field['field_type'] === 'built_in') {
                    $saveFields['fixed'][] = $field;
                } else {
                    $saveFields['custom'][] = $field;
                }
            }

            // 更新字段配置
            $this->modifyFieldConfig($item['code'], $saveFields);
        }

        // 更新模块字段缓存
        Redis::set(self::MODULE_CONFIG_REFRESH_KEY, time());
//        $this->generateModuleFieldCache();
    }

    /**
     * 输出模块的字段信息 md格式
     * @param $param
     * @return string
     */
    public function outputModuleTableFieldsWithFormat($param)
    {
        $moduleName = $param['module_code'] ?? null;
        $where = [];
        if (!empty($moduleName)) {
            $where['code'] = $moduleName;
        }
        $modules = model(ModuleModel::class)->where($where)->select();

        if (empty($modules)) {
            return '';
        }

        $dbName = C('database.connections')['mysql']['database'];
        $tableNameList = array_column($modules, 'code');
        $modules = array_column($modules, null, 'code');

        $tableNameList = array_map(function ($item) use (&$modules) {
            if ($modules[$item]['type'] === 'entity') {
                return '"entity"';
            }
            return '"' . $item . '"';
        }, $tableNameList);

        $tableComments = Db::query("SELECT TABLE_NAME,TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA='$dbName' AND TABLE_NAME IN (" . implode(',', $tableNameList) . ")");
        $tableComments = array_column($tableComments, 'table_comment', 'table_name');

        $fields = model(FieldModel::class)->where(['module_id' => ['IN', implode(',', array_column($modules, 'id'))]])->select();

        $fields = array_group_by($fields, 'module_id');
        $res = '';

        $fixedFieldsList = Db::query("SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='{$dbName}' AND TABLE_NAME IN (" . implode(',', $tableNameList) . ")");

        $fixedFieldsList = array_group_by($fixedFieldsList, 'table_name');
        $customFieldsLang = @file_get_contents(APP_PATH . '../database/custom_fields_lang.json');
        $customFieldsLang = json_decode($customFieldsLang, true) ?? [];

        foreach ($modules as $module) {
            $tableName = $module['type'] === 'fixed' ? $module['code'] : "entity";
            $fixedFields = $fixedFieldsList[$tableName] ?? [];
            $customFields = Module::$moduleDictData['field_index_by_code'][$module['code']]['custom'] ?? [];
            $moduleComment = $tableComments[$module['code']] ?? $customFieldsLang[$module['code']]['comment'] ?? $module['code'];


            $res .= PHP_EOL . PHP_EOL . PHP_EOL . '- ' . "{$module['code']} {$moduleComment}" . PHP_EOL . PHP_EOL;
            $fields = $fields[$module['id']] ?? [];
            $res .= '|字段名称|字段类型|字段描述|' . PHP_EOL . '| ------------ | ------------ |' . PHP_EOL;

            foreach ($fixedFields as $field) {
                $res .= '|' . $field['column_name'] . '|' . $field['data_type'] . '|' . htmlspecialchars($field['column_comment']) . '|' . PHP_EOL;
            }

            foreach ($customFields as $field) {
                $fieldComment = $customFieldsLang[$module['code']]['custom_fields'][$field['field']] ?? $field['lang'];
                $res .= '|' . $field['field'] . '|' . $field['type'] . '|' . $fieldComment . '|' . PHP_EOL;
            }
            $res .= '<hr />';
        }

        return $res;
    }

    /**
     * 获取实体属性自定义字段映射数据
     * @param $moduleId
     * @param string $keyType
     * @return array
     */
    public function getEntityAttributeCustomFieldsMapping($moduleId, $keyType = 'id')
    {
        $entityModuleAttributeCustomFieldsMapping = [];

        $entityAttributeCustomFields = model(FieldModel::class)->field("module_id,config,JSON_UNQUOTE(JSON_EXTRACT(config,'$.data_source.attribute_id')) as attribute_id")
            ->where([
                'type' => 'custom',
                'module_id' => $moduleId,
                "JSON_UNQUOTE(JSON_EXTRACT(config, '$.type'))" => 'attribute'
            ])
            ->cache(C('database.default_query_cache_time'))
            ->select();
        $attributeCodeList = [];
        if (count($entityAttributeCustomFields) > 0) {
            $attributeIdList = array_column($entityAttributeCustomFields, 'attribute_id', 'attribute_id');
            $attributeIdList = array_filter($attributeIdList, function ($item) {
                return intval($item) > 0;
            });
            if (count($attributeIdList) > 0) {
                $attributeCodeList = AttributeService::getInstance()->getAttributeList(['id' => ['IN', implode(',', $attributeIdList)]], 'id,code');
                $attributeCodeList = array_column($attributeCodeList, 'code', 'id');
            }
        }


        foreach ($entityAttributeCustomFields as $entityAttributeCustomField) {
            $entityAttributeCustomFieldConfig = json_decode($entityAttributeCustomField['config'], true);
            $attributeCode = $attributeCodeList[$entityAttributeCustomField['attribute_id']] ?? str_replace('attribute_', '', $entityAttributeCustomFieldConfig['data_source']['data']);
            $attributeConfig = [
                'field' => $entityAttributeCustomFieldConfig['field'],
                'attribute_id' => $entityAttributeCustomFieldConfig['data_source']['attribute_id'],
                'module_id' => $entityAttributeCustomFieldConfig['data_source']['module_id'],
                'attribute_code' => $attributeCode
            ];

            if ($keyType === 'code') {
                $entityModuleAttributeCustomFieldsMapping[$attributeCode] = $attributeConfig;
            } else {
                $entityModuleAttributeCustomFieldsMapping[$entityAttributeCustomFieldConfig['data_source']['attribute_id']] = $attributeConfig;
            }
        }

        ksort($entityModuleAttributeCustomFieldsMapping);

        return $entityModuleAttributeCustomFieldsMapping;
    }

    /**
     * 找到实体关联实体得自定义字段
     * @param $srcModuleId
     * @return array
     */
    public function getEntityRelationEntityCustomFields($srcModuleId)
    {
        $entityModuleIdData = model(ModuleModel::class)->field('id')->where(['type' => 'entity'])->select();
        $customFields = [];
        if ($srcModuleId > 0) {
            // 查找当前实体的自定义字段
            $itemValueStr = \generate_mysql_json_contains($entityModuleIdData, 'id');
            $fieldsConfigData = model(FieldModel::class)->field('config')->where([
                'is_horizontal' => 1,
                "type" => "custom",
                'module_id' => $srcModuleId,
                '_string' => "JSON_CONTAINS('[{$itemValueStr}]' , json_extract(config, '$.data_source.dst_module_id' ))"
            ])->select();
            if (!empty($fieldsConfigData)) {
                foreach ($fieldsConfigData as $fieldItemConfig) {
                    $fieldItem = json_decode($fieldItemConfig['config'], true);
                    if (!empty($fieldItem['data_source']['dst_module_id'])) {
                        $customFields[] = $fieldItem['field'];
                    }
                }
            }
        }
        return $customFields;
    }

    /**
     * 获取多个模块属性的映射
     * @param $moduleIds
     * @param string $keyType
     * @return array
     */
    public function getMultipleEntityAttributeCustomFieldsMapping($moduleIds, $keyType = 'id')
    {
        $entityModuleAttributeCustomFieldsMapping = [];

        $entityAttributeCustomFields = model(FieldModel::class)->field("module_id,config,JSON_UNQUOTE(JSON_EXTRACT(config,'$.data_source.attribute_id')) as attribute_id")
            ->where([
                'table' => 'entity',
                'type' => 'custom',
                'module_id' => ['IN', $moduleIds],
                "JSON_UNQUOTE(JSON_EXTRACT(config, '$.type'))" => 'attribute'
            ])
            ->cache(C('database.default_query_cache_time'))
            ->select();
        $attributeCodeList = [];
        $attributeIdList = [];
        if (!empty($entityAttributeCustomFields)) {
            foreach ($entityAttributeCustomFields as $entityAttributeCustomField) {
                if (!empty($entityAttributeCustomField['attribute_id'])) {
                    $attributeIdList[$entityAttributeCustomField['attribute_id']] = $entityAttributeCustomField['attribute_id'];
                }
            }
        }

        if (!empty($attributeIdList)) {
            $attributeCodeList = model(AttributeModel::class)->where(['id' => ['IN', implode(',', $attributeIdList)]])->field('id,code')->cache(C('database.default_query_cache_time'))->select();
            $attributeCodeList = array_column($attributeCodeList, 'code', 'id');
        }


        foreach ($entityAttributeCustomFields as $entityAttributeCustomField) {
            $entityAttributeCustomFieldConfig = json_decode($entityAttributeCustomField['config'], true);
            $attributeCode = $attributeCodeList[$entityAttributeCustomField['attribute_id']] ?? str_replace('attribute_', '', $entityAttributeCustomFieldConfig['data_source']['data']);
            $attributeConfig = [
                'field' => $entityAttributeCustomFieldConfig['field'],
                'attribute_id' => $entityAttributeCustomFieldConfig['data_source']['attribute_id'],
                'module_id' => $entityAttributeCustomFieldConfig['data_source']['module_id'],
                'attribute_code' => $attributeCode
            ];

            if ($keyType === 'code') {
                $entityModuleAttributeCustomFieldsMapping[$entityAttributeCustomField['module_id']][$attributeCode] = $attributeConfig;
            } else {
                $entityModuleAttributeCustomFieldsMapping[$entityAttributeCustomField['module_id']][$entityAttributeCustomFieldConfig['data_source']['attribute_id']] = $attributeConfig;
            }
        }

        foreach ($entityModuleAttributeCustomFieldsMapping as $entityModuleAttributeCustomFieldsMappingItem) {
            ksort($entityModuleAttributeCustomFieldsMappingItem);
        }

        return $entityModuleAttributeCustomFieldsMapping;
    }

    /**
     * 检查是否需要刷新字段缓存
     * @return void
     * @throws \Exception
     */
    public static function checkFieldNeedUpdate()
    {
        $lastFreshFieldTime = intval(Redis::get(self::MODULE_CONFIG_REFRESH_KEY));
        if ($lastFreshFieldTime <= 0 || $lastFreshFieldTime <= self::$moduleFieldMakeTime) {
            return;
        }

        try {
            $flag = Module::refreshModuleConfig();

            self::clearModelFields();

            if ($flag) {
                self::$moduleFieldMakeTime = time();
            }
        } catch (\Throwable $e) {
            trace(format_exception_info($e), 'ERR');
        }
    }

    /**
     * 清空模块字段缓存
     * @return void
     */
    public static function clearModelFields()
    {
        $modelDir = dir('./app/common/model');
        $baseClassName = 'common\\model\\';

        // 处理直接model(xxxModel::class)的缓存 这种的是common\\model\\xxxModel
        while ($file = $modelDir->read()) {
            $fileName = substr($file, 0, strlen($file) - 4);
            $className = $baseClassName . $fileName;
            if (!class_exists($className)) {
                continue;
            }
            try {
                $m = model($className);
                if ($m instanceof Model) {
                    $m->flush('', true);
                    trace(date("Y-m-d") . $className . ' flushed', 'DEBUG');
                }
            } catch (\Throwable $e) {
                trace(format_exception_info($e), 'ERR');
            }
        }
        // 处理commonService 里的moduleObject
        CommonService::flushModelFields();
    }
}
